package main

import (
	"deploy/schema"
	"deploy/utils"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/matishsiao/goInfo"
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

var Conf *utils.ClientConf
var serverUrl string

// 设置可启动多个agent
var AgentId string

func parseCmdLineFlags() {
	//flag.BoolVar(&flgHelp, "help", false, "if true, show help")
	flag.StringVar(&AgentId, "n", "", "指定实例名")
	flag.Parse()
}

func init() {
	log.Println("启动中...")
	parseCmdLineFlags()
	var err error
	if utils.FileExist("./download") == false {
		log.Println("创建下载文件目录 ./download/")
		err = os.Mkdir("./download", 0755)
		if err != nil {
			log.Println("创建目录异常：", err.Error())
			os.Exit(11)
		}
	}
	Conf, err = utils.GetClientConfiguration("etc/client.yaml")
	if err != nil {
		log.Println("加载配置出错", err.Error())
		os.Exit(0)
	}
	ss := strings.Split(Conf.Server, "/")
	if len(ss) == 4 {
		serverUrl = fmt.Sprintf("http://%s", ss[2])
	}
	log.Println("serverUrl:", serverUrl)
	log.Println("配置加载成功")
}

type Client struct {
	HeartInterval int             // 心跳间隔
	ws            *websocket.Conn // 连接套接字
	Id            int             // 用户身份信息
	Name          string          // 用户身份信息
	mutex         sync.Mutex      // 避免重复关闭管道
	isClosed      bool            // 链接是否关闭
	closeChan     chan bool       // 关闭通知
	outChan       chan []byte     // 发送数据
}

func NewWs(wsUrl string, heartInterval int) *Client {
	conn, _, err := websocket.DefaultDialer.Dial(wsUrl, nil)
	if err != nil {
		log.Println(err.Error())
		return nil
	}
	return &Client{HeartInterval: heartInterval,
		ws:        conn,
		Name:      utils.GetHostName(),
		isClosed:  false,
		closeChan: make(chan bool, 1),
		outChan:   make(chan []byte),
	}
}

// 发送心跳检查
func (c *Client) Heart() {
	for {
		msg := utils.NewMessage("12345678", 1, []byte(`{"key":"ping1111"}`))
		//log.Println("发送数据长度为",len(msg))
		c.outChan <- msg
		time.Sleep(time.Second * time.Duration(c.HeartInterval))
	}
}

// 声明自己的名字
func (c *Client) UpdateId() {
	agentInfo, err := goInfo.GetInfo()
	if err != nil {
		log.Println(err)
		os.Exit(911)
	}
	HostName := utils.GetHostName()
	HardwareAddr := fmt.Sprintf("%s", utils.GetNetHadr())
	if AgentId != "" {
		HostName = AgentId
		HardwareAddr = utils.RandomAddr()
	}
	cim := utils.ClientInfoMessage{
		HostName:       HostName,
		AgentVersion:   utils.GetAgentVersion(),
		HardwareAddr:   HardwareAddr,
		SystemVersion:  agentInfo.Core,
		System:         agentInfo.OS,
		SystemPlatform: agentInfo.Platform,
		Kernel:         agentInfo.Kernel,
		ServerType:     utils.GetConfType(),
	}
	log.Printf("向server发送更名为%s请求\n", cim)
	cj := utils.ToJson(&cim)
	//log.Println(cim.ToJson())
	if cj != nil {
		msg := utils.NewMessage("12345678", 4, cj)
		c.outChan <- msg
	} else {
		log.Println("改名前转换为json失败")
	}
}

func (c *Client) Send() {
	for {
		msg := <-c.outChan
		err := c.ws.WriteMessage(websocket.BinaryMessage, msg)
		if err != nil {
			log.Println("连接发生错误", err)
			c.closeChan <- true
			return
		}
		//time.Sleep(time.Second*10)
	}
}

func main() {
	for {
		client := NewWs(Conf.Server, 3)
		// 启动心跳检查
		if client == nil {
			time.Sleep(time.Second * 5)
			continue
		}
		go client.Send()    // 向server发送数据
		go client.Receive() // 接受server的数据
		client.UpdateId()
		//go client.Heart() // 定时心跳
		select {
		case <-client.closeChan:
			client.isClosed = true
			log.Println("连接已被关闭~")
			log.Println("正在重新建立连接~")
			continue
		}
	}
}

// 接收这个用户的数据 调用数据处理的方法
func (c *Client) Receive() {
	for {
		_, buf, err := c.ws.ReadMessage()
		if err != nil {
			log.Println("读取数据出错:", err.Error())
			c.mutex.Lock()
			c.ws.Close()
			c.closeChan <- true
			c.mutex.Unlock()
			return
		}
		log.Printf("收到数据长度为：%d", len(buf))
		m := utils.NewRequest(buf, []byte("12345678"))
		if m == nil {
			log.Println("消息出错了```")
			continue
		}
		c.Domsg(*m)
	}
}

func (c *Client) Domsg(message utils.Message) {
	switch message.MsgType {
	case 1:
		//log.Println("ping/pong")
	case 2:
		log.Println("work")
		j := utils.JobMessage{}
		err := json.Unmarshal(message.Msg, &j)
		if err != nil {
			log.Println(err.Error())
			return
		}
		utils.DoJob(&j, c.outChan)
	case 5:
		j := utils.StrMessage{}
		err := json.Unmarshal(message.Msg, &j)
		if err != nil {
			log.Println(err.Error())
			return
		}
		log.Println(j.Str)
	case 8: // listdir
		j := utils.StrMessage{}
		err := json.Unmarshal(message.Msg, &j)
		if err != nil {
			log.Println(err.Error())
			return
		}
		utils.ListDir(j.Str, c.outChan) // 查看任务，把结果 把结果 回复给server
	case 9: // 上传文件
		j := utils.StrMessage{}
		err := json.Unmarshal(message.Msg, &j)
		if err != nil {
			log.Println(err.Error())
			return
		}
		utils.UploadAFile(serverUrl, j.Str, c.outChan) //把结果 把结果 回复给server ,并且执行上传任务
	case 10: // 获取配置文件的内容
		j := schema.AgentConf{}
		err := json.Unmarshal(message.Msg, &j)
		if err != nil {
			log.Println(err.Error())
			return
		}
		utils.GetConfData(&j, c.outChan) //把结果 把结果 回复给server ,并且执行上传任务
	case 11: // 上传指定时间点和目录的日志
		j := schema.GetLogByTimeSchema{}
		err := json.Unmarshal(message.Msg, &j)
		if err != nil {
			log.Println(err.Error())
			return
		}
		utils.UploadFileByTime(serverUrl, &j, c.outChan)
	case 100: // 特定的命令
		func() {}()
	default:
		log.Println("啥也不想干了")
	}
}
